import csv , os
import math
import Paras
from DataSheetClass import DataSheet
from CsvDatas import CsvDatas
import numpy as np
from shapely.geometry import Point as ShapelyPoint, Polygon
from DataBaseClass import DataBase
from typing import List
from PIL import Image, ImageTk
import tkinter as tk

class OperationFeature:
    # 靠近对象颜色,表示单击可以选中
    nearby_color = '#5DF43F'
    # 选中对象颜色
    catch_color = '#FE9900'
    def __init__(self):        
        self.catchable:bool = False
        self.catched:bool = False
        self.nearby:bool = False
        self.visible:bool = True

# 坐标类
class Point(OperationFeature):
    def __init__(self, x=0.0, y=0.0,catchable:bool = True):
        super().__init__()
        self.x:float = x
        self.y:float = y
    
    def scare(self, rate):
        """绝对坐标缩放"""
        self.x *= rate
        self.y *= rate
        return self
    
    def updown(self):
        """上下颠倒"""
        self.y *= -1    
        return self
    
    def mirror(self):
        """左右镜像"""
        self.x *= -1    
        return self
    
    def padding(self, pad_x, pad_y):
        """增加padding"""
        self.x += pad_x
        self.y += pad_y     
        return self
    
    def set(self, point):
        """获得新坐标"""
        self.x = point.x
        self.y = point.y
        return self
    
    def caculate_distance_from_point(self, point):
        """计算与点之间的距离"""
        return math.sqrt((self.x - point.x) ** 2 + (self.y- point.y) ** 2)
    
    def set_visible(self, visible:bool):
        self.visible = visible
    def set_catchable(self, catchable:bool):
        self.catchable = catchable        
    def set_catched(self, catched:bool):
        self.catched = catched
    def is_equal_to(self, point):
        return self.x == point.x and self.y == point.y
    def preview(self,canvas,radius = 2, linecolor='',fillcolor='blue'):
        theradius = radius
        if self.visible :
            if self.catched:
                color = OperationFeature.catch_color      
                theradius = radius + 2                
            elif self.nearby:
                color = OperationFeature.nearby_color   
                theradius = radius + 2
            else:
                color = linecolor
                                   
            canvas.create_oval(self.x - theradius, self.y - theradius, self.x + theradius, self.y + theradius,
                    fill=fillcolor, outline=color)
            
    def to_tuple(self):
        return (self.x, self.y)
    
    
    @staticmethod
    def Copy(p):
        """复制并得到一个新坐标"""
        return Point(p.x, p.y)
    
    @staticmethod
    def Rotate(point, angle_degrees, origin):
        """逆时针旋转"""
        try:
            # Rotate a point around an origin by a given angle in degrees
            angle_radians = math.radians(float(angle_degrees))
            cos_theta = math.cos(angle_radians)
            sin_theta = math.sin(angle_radians)
            ox, oy = origin.x, origin.y
            px, py = point.x, point.y
            qx = ox + cos_theta * (px - ox) - sin_theta * (py - oy)
            qy = oy + sin_theta * (px - ox) + cos_theta * (py - oy)
            return Point(qx, qy)
        except Exception as e:
            raise ValueError(f"rotate err : point: {point.x},{point.y} angle_degrees: {angle_degrees} origin: {origin.x},{origin.y}")
    
    def rotate(self, angle_degrees, origin) :
        """逆时针旋转"""
        try:
            # Rotate a point around an origin by a given angle in degrees
            angle_radians = math.radians(float(angle_degrees))
            cos_theta = math.cos(angle_radians)
            sin_theta = math.sin(angle_radians)
            ox, oy = origin.x, origin.y
            px, py = self.x, self.y
            qx = ox + cos_theta * (px - ox) - sin_theta * (py - oy)
            qy = oy + sin_theta * (px - ox) + cos_theta * (py - oy)
            self.x = qx
            self.y = qy
            return self
        except Exception as e:
            raise ValueError(f"rotate err : point: {self.x},{self.y} angle_degrees: {angle_degrees} origin: {origin.x},{origin.y}")
        

#矩形的顶点类
class Vertices(OperationFeature):
    def __init__(self, p0=Point(), p1=Point(), p2=Point(), p3=Point()):
        super().__init__()
        # Initialize the four vertices of a rectangle
        self.p0 = p0 #基于原始坐标的左下顶点
        self.p1 = p1 #基于原始坐标的左上顶点
        self.p2 = p2 #基于原始坐标的右上顶点
        self.p3 = p3 #基于原始坐标的右下顶点
        

    def set (self, newvs):
        self.p0.set(newvs.p0)
        self.p1.set(newvs.p1)
        self.p2.set(newvs.p2)
        self.p3.set(newvs.p3)
        return self
    
    def set_visible(self, visible:bool):
        self.visible = visible
    def preview(self, canvas, linecolor='blue', fillcolor='', linewidth=2):
        """
        预览这个框不包括顶角的点
        """
        # 检查平台是否设置为可见
        if self.visible:
            # 构建平台四个点的坐标列表
            platform_points = [
                self.p0.x, self.p0.y,
                self.p1.x, self.p1.y,
                self.p2.x, self.p2.y,
                self.p3.x, self.p3.y
            ]
            # 在画布上绘制平台
            if self.catched:
                color = OperationFeature.catch_color      
                linewidth += 2
            if self.nearby:
                color = OperationFeature.nearby_color
                linewidth += 2
            else:
                color = linecolor 
            canvas.create_polygon(platform_points, outline=color, fill=fillcolor, width=linewidth)
    def Points(self):
        """
        获取构成线段的四个点的坐标。
        """
        return [self.p0, self.p1, self.p2, self.p3]

    def is_point_inside(self, point):
        # 定义四边形的四个顶点坐标
        polygon_vertices = [self.p0.to_tuple(), self.p1.to_tuple(), self.p2.to_tuple(), self.p3.to_tuple()]

        # 创建一个Polygon对象
        polygon = Polygon(polygon_vertices)

        # 定义要检查的点
        point_to_check = ShapelyPoint(point.x, point.y)

        # 检查点是否在多边形内部
        is_inside = polygon.contains(point_to_check)
        
        return is_inside

    def caculate_center(self):        
        min_x = min(self.p0.x, self.p1.x, self.p2.x, self.p3.x)
        max_x = max(self.p0.x, self.p1.x, self.p2.x, self.p3.x)
        min_y = min(self.p0.y, self.p1.y, self.p2.y, self.p3.y)
        max_y = max(self.p0.y, self.p1.y, self.p2.y, self.p3.y)
        return Point((min_x + max_x) / 2 , (min_y + max_y) / 2)
    
    @staticmethod
    def Copy(vs):
        return Vertices(vs.p0,vs.p1,vs.p2,vs.p3)

#芯片的PAD类
class PAD(OperationFeature):
    def __init__(self, platform,name="PAD",type="P0PAD", pos=Point(), w=1, h=1, wire_dropfull=True):
        super().__init__()
        # Initialize a PAD with its name, position, width, and height
        self.name = name #pad name
        self.platform = platform #记录自己所属的platform
        self.pos = Point.Copy(pos) #相对于platform.pos的position
        self.w = w #水平宽度
        self.h = h #垂直高度
        self.type = type # PAD 类型 ： CPAD(pos为PAD中心点坐标)， P0PAD(pos为PAD左下角坐标)
        self.wire_dropfull = wire_dropfull # Faults ： 不允许打线的PAD
        self.c:Point = Point()
        if self.type == "P0PAD": # 只有P0PAD类型才是直接的P0坐标 
            self.vertices:Vertices = Vertices(
                    Point(self.pos.x         , self.pos.y),          #基于原始坐标的左下顶点
                    Point(self.pos.x         , self.pos.y + self.h), #基于原始坐标的左上顶点
                    Point(self.pos.x + self.w, self.pos.y + self.h), #基于原始坐标的右上顶点
                    Point(self.pos.x + self.w, self.pos.y),          #基于原始坐标的右下顶点
            )  
            self.vs:Vertices = Vertices(
                    Point(self.pos.x         , self.pos.y),          #基于原始坐标的左下顶点
                    Point(self.pos.x         , self.pos.y + self.h), #基于原始坐标的左上顶点
                    Point(self.pos.x + self.w, self.pos.y + self.h), #基于原始坐标的右上顶点
                    Point(self.pos.x + self.w, self.pos.y),          #基于原始坐标的右下顶点
            )    
        else :      
            self.vertices:Vertices = Vertices(
                    Point(self.pos.x - self.w/2, self.pos.y - self.h/2), #基于原始坐标的左下顶点
                    Point(self.pos.x - self.w/2, self.pos.y + self.h/2), #基于原始坐标的左上顶点
                    Point(self.pos.x + self.w/2, self.pos.y + self.h/2), #基于原始坐标的右上顶点
                    Point(self.pos.x + self.w/2, self.pos.y - self.h/2), #基于原始坐标的右下顶点
            )  
            self.vs:Vertices = Vertices(
                    Point(self.pos.x - self.w/2, self.pos.y - self.h/2), #基于原始坐标的左下顶点
                    Point(self.pos.x - self.w/2, self.pos.y + self.h/2), #基于原始坐标的左上顶点
                    Point(self.pos.x + self.w/2, self.pos.y + self.h/2), #基于原始坐标的右上顶点
                    Point(self.pos.x + self.w/2, self.pos.y - self.h/2), #基于原始坐标的右下顶点
            )    
        
    
    def set_vs(self, vs:Vertices) :
        self.vs.set(vs)
        self.c.set(self.vs.caculate_center()) 
    
    def is_point_inside(self, point):
        if self.wire_dropfull :
            return self.vs.is_point_inside(point)
        else:
            return False
    
    
    def closeby(self):
        self.vs.nearby = True
        
    def awayfrom(self):
        self.vs.nearby = False
        
    def preview(self, canvas,linecolor='red',fillcolor='',linewidth=2,fontsize=10,fontcolor='blue',):
        # Draw the PAD on the given canvas
        if self.visible:
            if self.vs :
                self.vs.preview(canvas,linecolor=linecolor,fillcolor=fillcolor,linewidth=linewidth)
                # Display the name at the center point
                canvas.create_text(self.c.x, self.c.y, text=self.name, fill=fontcolor, font=("Helvetica", fontsize))
            if self.c :
                self.c.preview(canvas=canvas,radius = 2,fillcolor=linecolor)

class Platform(OperationFeature):

    def __init__(self, file_path):
        super().__init__()
        self.db:DataBase = DataBase(file_path)
        if self.db.datasheet.current_sheet :    
            for row in self.db.datasheet.current_sheet.rows[1:]:
                self.imagepath = ""
                self.image = None
                self.image_view:Point = Point(0,0)
                
                if row[0] == 'IMAGE' :
                    imgpath = row[1]
                    if ":" not in imgpath:
                        imgpath = os.path.join(self.db.rootpath, imgpath)
                    if os.path.exists(imgpath) :
                        self.imagepath = imgpath # 图片路径
                                    
                else :
                    
                    if len(row) != 6:
                        raise ValueError(f"Invalid row format: {row} len={len(row)} file_path={file_path}")
                    
                    category, name, x, y, w, h = row
                    
                    try:
                        x, y, w, h = float(x), float(y), float(w), float(h)
                    except ValueError as e:
                        raise ValueError(f"Error converting values to floats: {e} in row: {row}")
                    
                    if category == 'Platform':
                        self.init(name, Point(x, y), w, h)
                    
                    elif category == 'P0PAD' or category == 'CPAD' or category == 'LFDROP':
                        #xy是左下角的PAD
                        pad = PAD(name=name, platform=self, type=category, pos=Point(x, y), w=w, h=h)
                        pad.catchable = False
                        self.pads.append(pad)
                        
                        
                    elif category == 'LF':
                        #LF xy必定需在中心
                        pad = PAD(name=name, platform=self, type=category, pos=Point(x, y), w=w, h=h, wire_dropfull= False) # 只用来显示区域，不用来打线，打线用'LFDROP'PAD
                        #不给捕获
                        pad.catchable = False
                        self.pads.append(pad)  
                        
                    else:
                        raise ValueError(f"Unknown category: {category} in row: {row}")                    
            
            
    def init(self, name, pos, w, h, rotatedegree=0, pads=None,file_path=""):
        # Initialize a Platform with its name, position, width, height, rotation degree, and optional list of PADS
        self.name = name #平台（Package或者Chip）
        self.w = w #水平宽度
        self.h = h #垂直高度
        self.rotatedegree = rotatedegree #顺时针旋转角度
        self.pads = pads if pads else [] #PAD列表
        self.pos:Point = None
        self.vertices:Vertices = Vertices()
        self.vs = Vertices()
        self.set_pos(pos)
        
    def set_pos(self, pos:Point):
        self.pos = pos #画布上的绝对坐标
        self.vertices:Vertices = Vertices(
            Point(self.pos.x         , self.pos.y),          #基于原始坐标的左下顶点
            Point(self.pos.x         , self.pos.y + self.h), #基于原始坐标的左上顶点
            Point(self.pos.x + self.w, self.pos.y + self.h), #基于原始坐标的右上顶点
            Point(self.pos.x + self.w, self.pos.y),          #基于原始坐标的右下顶点
        ) 
        self.vs.set(self.vertices)
        
    def reshape(self, paras:Paras): 
        
        # Calculate the vertices of the Platform after rotation and scaling
        # 原点在原始参数的左下角self.pos但不是画布的左下角
        # 旋转，基于原始数据计算旋转
        # 缩放
        # y变-y
        # pad， 画布留白，加pad参数
        
        #p0就是orgin
        self.vs.set(self.vertices)
        self.vs.p0.rotate(self.rotatedegree, self.pos).scare(paras.scale_factor).updown().padding(paras.pad_x,paras.pad_y)
        self.vs.p1.rotate(self.rotatedegree, self.pos).scare(paras.scale_factor).updown().padding(paras.pad_x,paras.pad_y)
        self.vs.p2.rotate(self.rotatedegree, self.pos).scare(paras.scale_factor).updown().padding(paras.pad_x,paras.pad_y)
        self.vs.p3.rotate(self.rotatedegree, self.pos).scare(paras.scale_factor).updown().padding(paras.pad_x,paras.pad_y)

        # Calculate the vertices of each PAD after rotation and scaling
        for pad in self.pads:
            pad.set_vs(Vertices(
                Point.Copy(pad.vertices.p0).padding(self.pos.x,self.pos.y).rotate(self.rotatedegree, self.pos).scare(paras.scale_factor).updown().padding(paras.pad_x,paras.pad_y),
                Point.Copy(pad.vertices.p1).padding(self.pos.x,self.pos.y).rotate(self.rotatedegree, self.pos).scare(paras.scale_factor).updown().padding(paras.pad_x,paras.pad_y),
                Point.Copy(pad.vertices.p2).padding(self.pos.x,self.pos.y).rotate(self.rotatedegree, self.pos).scare(paras.scale_factor).updown().padding(paras.pad_x,paras.pad_y),
                Point.Copy(pad.vertices.p3).padding(self.pos.x,self.pos.y).rotate(self.rotatedegree, self.pos).scare(paras.scale_factor).updown().padding(paras.pad_x,paras.pad_y)
            ))
            
        #如果有图 画图
        if self.imagepath != "":
            # 打开原始图像并转换为 RGBA 格式
            original_image = Image.open(self.imagepath).convert("RGBA")
            
            # 缩放图像
            scaled_width = int(self.w * paras.scale_factor)
            scaled_height = int(self.h * paras.scale_factor)
            resized_image = original_image.resize((scaled_width, scaled_height), Image.LANCZOS)
            
            # 旋转图像            
            self.image = ImageTk.PhotoImage(resized_image.rotate(self.rotatedegree, expand=True))
            self.image_view.x = min(self.vs.p0.x, self.vs.p1.x, self.vs.p2.x, self.vs.p3.x)
            self.image_view.y = min(self.vs.p0.y, self.vs.p1.y, self.vs.p2.y, self.vs.p3.y)
            

    def preview(self, paras:Paras):
        
        #有图画图
        if self.image :
            paras.canvas.create_image(self.image_view.x, self.image_view.y, anchor=tk.NW, image=self.image)
        
        # Draw the Platform on the given canvas
        self.vs.preview(paras.canvas,'blue')
        self.vs.p0.preview(paras.canvas,6,'blue')

        # Draw all PADS of the Platform on the given canvas
        for pad in self.pads:
            pad.preview(paras.canvas,'red','',2,paras.fontsize,'blue')
            

class WireDrop(OperationFeature):
    def __init__(self, pad: PAD = None,isCPoint:bool = False, point:Point = Point(0,0)):
        super().__init__()
        self.isCPoint = isCPoint #True表示point是某个点而不是PAD，False表示point是pad的中心点
        self.pad = pad        
        self.point = Point.Copy(point)
        self.catchable = True
    
    def caculate_end(self):       
        #如果这个wire drop的目标不是pad里面的点而是pad，则计算pad的中心点给point 
        if not self.isCPoint:            
            try:
                self.point.set(self.pad.c)
            except ValueError as e:
                raise ValueError(f"Error getting wire end drop of pad: {e} {self.pad.platform.name} {self.pad.name}")
    def is_equal_to_end(self, end):
        return self.point.is_equal_to(end.point)

    def is_equal_to_poing(self, point):
        return self.point.is_equal_to(point)
    
    def closeby(self):
        self.point.visible = True
        self.point.nearby = True    
    def awayfrom(self):
        self.point.visible = False
        self.point.nearby = False
    def preview(self, canvas, outlinecolor='blue',radius=2):
        self.point.preview(canvas,linecolor=outlinecolor)
    
	

class BondingWire(OperationFeature):
    database : list[str]
    def __init__(self, end0:WireDrop, end1:WireDrop, database):
        super().__init__()
        # Initialize a wire with two endpoints
        self.end0:WireDrop = end0 # 线的端点允许捕获
        self.end1:WireDrop = end1 # 线的端点允许捕获
        self.nearbyend:WireDrop = None # 把closeby的end保存起来
        self.catchingend:WireDrop = None # 如果被抓取了就把被抓取的端点存起来
        self.catchingpoint:Point = Point(0,0,catchable=False)
        self.database = database
    
    def catch(self):
        self.catchingend = self.nearbyend
        self.catchingpoint.set(self.nearbyend.point)
        self.catched = True
        
    def release(self):
        self.catched = False
        
    def release_on_pad(self, pad:PAD):
        self.release()
        newdrop = WireDrop(pad=pad)
        if self.catchingend == self.end0 :
            self.end0 = newdrop
        else :
            self.end1 = newdrop
        
        # update database data
        self.database[1] = self.end0.pad.platform.name
        self.database[2] = self.end0.pad.name
        self.database[3] = self.end1.pad.platform.name
        self.database[4] = self.end1.pad.name

    
    def closeby(self):
        self.nearby = True
        self.nearbyend.closeby()
        if self.nearbyend == self.end0 :
            self.end1.awayfrom()
        else :
            self.end0.awayfrom()
        
    def awayfrom(self):
        self.end1.awayfrom()
        self.end0.awayfrom()
        self.nearby = False

    def catch_and_move(self, point: Point):
        self.catchingpoint.set(point)
		
    def preview(self, canvas,linecolor='green',linewidth=2):
        self.end0.caculate_end()
        self.end1.caculate_end()            
        
        point0 = self.end0.point
        point1 = self.end1.point

        if self.catchable and self.catched:       
            if self.nearbyend.is_equal_to_end(self.end0):
                point0 = self.catchingpoint
            elif self.nearbyend.is_equal_to_end(self.end1):
                point1 = self.catchingpoint
            else :
                raise ValueError(f"wire cache no point ")
            
        outlinecolor = linecolor
        if self.catched:  
            outlinecolor = OperationFeature.catch_color    
            linewidth += 2
        elif self.nearby:
            outlinecolor = OperationFeature.nearby_color
            linewidth += 2
            
        # Draw the wire on the given canvas
        canvas.create_line(point0.x, point0.y, point1.x, point1.y, fill=outlinecolor, width=linewidth)
        
        if self.nearby :
            self.nearbyend.preview(canvas,outlinecolor=outlinecolor,radius=3)
        
    def point_to_segment_distance(self, point, segment_start, segment_end):
        # 将输入转换为numpy数组以便于向量运算
        p = np.array(point)
        start = np.array(segment_start)
        end = np.array(segment_end)

        # 向量v是线段的方向向量
        v = end - start
        
        if np.all(v == 0):  # 线段退化成一个点
            return np.linalg.norm(p - start)  # 返回点到该点的距离

        # 计算垂线方向上的单位向量
        v_perp = np.array([-v[1], v[0]])  # v的法向量
        v_perp = v_perp / np.linalg.norm(v_perp)  # 法向量归一化
        
        # 向量w是从线段起点指向点p的向量
        w = p - start

        # 计算投影长度（即垂足在v上的位置）
        projection_length = np.dot(w, v) / np.dot(v, v)

        # 如果投影在start-end线段之外，则最近点是线段的一个端点
        if projection_length < 0:
            plv = start
        elif projection_length > 1:
            plv = end
        else:
            # 否则，计算垂足的位置
            plv = start + projection_length * v

        # 计算并返回点到垂足的距离
        return np.linalg.norm(p - plv)
    def caculate_distance_from_point(self, point: Point) -> float:
        return self.point_to_segment_distance((point.x,point.y),(self.end0.point.x,self.end0.point.y),(self.end1.point.x,self.end1.point.y))
    def caculate_hand_distance(self, point:Point):
        """
        计算抓手离自己的距离,计算的时候也要计算如果要被cache的话是哪个end被抓住
        返回hand距离自己的距离
            
        """
        if self.catchable :
            dis0 = self.end0.point.caculate_distance_from_point(point)
            dis1 = self.end1.point.caculate_distance_from_point(point)
            self.nearbyend = self.end0 if dis0 < dis1 else self.end1
            return self.caculate_distance_from_point(point)
        else :
            return 99999999.0
        

    @staticmethod
    def caculate_midpoint(point1:Point, point2:Point)->Point:
        """计算wire的中间点"""
        mid_x = (point1.x + point2.x) / 2
        mid_y = (point1.y + point2.y) / 2
        return Point(mid_x, mid_y)

class Package(OperationFeature):
    
    bondingtable_dbs: List[DataBase]  # 类型注解
        
    def __init__(self, platform_file_paths, bonding_table_file_paths):
        super().__init__()
        # Initialize an empty list of Platforms and Wires
        self.platforms = [] #package和芯片信息
        self.bondingtable_dbs = [] # 保存bonndingtable原始数据
        self.wires = []    #打线关系
        self.stacking = [] #堆叠顺序
        self.extract_platforms(platform_file_paths)       
        self.extract_bondingtable(bonding_table_file_paths) 
        self.catched_wire:BondingWire = None #记录当前被catched的wire
        self.catching:bool = False
        self.near_wire:BondingWire = None #记录当前附近的wire
        self.near_wire_changed:bool = False
        self.in_pad_now:PAD = None
        self.inside_pad_changed:bool = False
        
    def hand_near_any_wire(self, point : Point, near_condition_distance=20):
        wire_distances = {}
        for wire in self.wires:
            wire_distances[wire] = wire.caculate_hand_distance(point)
        mindistance = min(wire_distances.values())
        
        self.near_wire_changed = False
        if mindistance < near_condition_distance :
            new_near_wire = None
            for w,dist in wire_distances.items():
                if dist == mindistance:
                    new_near_wire = w
            if new_near_wire != self.near_wire :
                if self.near_wire != None :
                    self.near_wire.awayfrom()
                self.near_wire = new_near_wire                    
                self.near_wire.closeby()
                self.near_wire_changed = True # 用来表示近距离的线对象改变了，需要重绘
            else :
                self.near_wire_changed = False # near对象没有改变
            return True # 有nearby的wire

        else :
            if self.near_wire != None :
                self.near_wire.awayfrom()
                self.near_wire_changed = True
            else :
                self.near_wire_changed = False #本来也没有near对象
            self.near_wire = None
            return False # 没有nearby的wire
    
    def hand_catch_near_wire(self) :
        self.hand_release_wire()
        self.catched_wire = self.near_wire
        self.catched_wire.catch()
                
    def hand_release_wire(self) :
        if self.catched_wire != None :
            self.catched_wire.release()
            self.catched_wire = None
            
    def hand_release_wire_on_pad(self) :
        if self.catched_wire != None :
            self.catched_wire.release_on_pad(self.in_pad_now)
            self.catched_wire = None
    def hand_catch_wire_move(self, point: Point):
        self.catched_wire.catch_and_move(point)
        
    def hand_in_any_pad(self, point : Point) :
        # Calculate vertices with scaling before drawing
        for platform in self.platforms:
            for pad in platform.pads :
                if pad.is_point_inside(point) :
                    if self.in_pad_now != pad :
                        if self.in_pad_now != None :
                            self.in_pad_now.awayfrom()
                        self.in_pad_now = pad
                        self.in_pad_now.closeby()
                        self.inside_pad_changed = True # 对象变更
                    else :
                        self.inside_pad_changed = False  # 对象没变更                   
                    return True
        
        self.hand_dont_care_pads() 
        return False
    
    def hand_dont_care_pads(self):            
        if self.in_pad_now != None :
            self.in_pad_now.awayfrom()
            self.inside_pad_changed = True  # 对象变更
            self.in_pad_now = None
        else:
            self.inside_pad_changed = False # 对象没变更      


    def extract_platforms(self, file_paths):
        # Clear existing platforms and load new ones from CSV files
        self.platforms.clear()
        for file_path in file_paths:
            platform = Platform(file_path)
            if platform:
                self.platforms.append(platform)
              

    #根据name找到platform对象         
    def get_platform_by_name(self, name):
        for platform in self.platforms:
            if platform.name == name:
                return platform
        return None  # 如果没有找到匹配的平台，返回None    
    
    def save_bondingtable(self):
        for db in self.bondingtable_dbs:
            db.update_source()
    def extract_bondingtable(self, file_paths):
        # Clear existing wires and load new ones from CSV files
        
        self.bondingtable_dbs.clear()
        self.wires.clear()
        
        for file_path in file_paths:
            try:
                bondingtable = DataBase(file_path)        
            except FileNotFoundError:
                print(f"File not found: {file_path}")
            except PermissionError:
                print(f"Permission denied: {file_path}")
            except Exception as e:
                print(f"An error occurred: {e}")
            
            if bondingtable.datasheet.current_sheet :
                
                self.bondingtable_dbs.append(bondingtable)
                
                for row in bondingtable.datasheet.current_sheet.rows[1:]:
                    Feature = row[0]
                    if Feature == 'PAD2PAD':
                        #wire PAD to PAD                            
                        Feature, end0PlatformName, end0PADName, end1PlatformName, end1PADName = row
                        if end0PlatformName == '':
                            continue
                        if end0PADName == '':
                            continue
                        if end1PlatformName == '':
                            continue
                        if end1PADName == '':
                            continue
                        
                        #print(f"wire : {end0Platform} {end0PADName} {end1Platform} {end1PADName}")
                        end0Platform = next((p for p in self.platforms if p.name == end0PlatformName), None)
                        end1Platform = next((p for p in self.platforms if p.name == end1PlatformName), None)
                        
                        if not end0Platform or not end1Platform:
                            raise ValueError(f"Platform not found for end0PlatformName: {end0PlatformName} or end1PlatformName: {end1PlatformName}")
                        
                        try:
                            end0PAD = next((p for p in end0Platform.pads if p.name == end0PADName), None)
                        except ValueError as e:
                            raise ValueError(f"Error getting PAD A center: {e} row={rowid} {end0Platform.name} {end0PADName} {end1Platform.name} {end1PADName}  len(row)={len(row)}")
                        
                        try:
                            end1PAD = next((p for p in end1Platform.pads if p.name == end1PADName), None)
                        except ValueError as e:
                            raise ValueError(f"Error getting PAD B center: {e} row={rowid} {end0Platform.name} {end0PADName} {end1Platform.name} {end1PADName}  len(row)={len(row)}")
                        
                        drop0 = WireDrop(pad=end0PAD)
                        drop1 = WireDrop(pad=end1PAD)
                        wire = BondingWire(drop0, drop1, row)
                        wire.catchable = True # 线都允许被抓取
                        self.wires.append(wire)
                        
                    elif Feature == 'PLACEMENT':
                        Feature, platformName,offsetx,offsety,rotate = row                            
                        if platformName == '':
                            continue                     
                        if offsetx == '':
                            continue                     
                        if offsety == '':
                            continue                     
                        if rotate == '':
                            rotate = 0
                            
                        platform:Platform = next((p for p in self.platforms if p.name == platformName), None)
                        if not platform:
                            raise ValueError(f"Platform not found for platformName: {platformName}")
                        
                        platform.set_pos(Point(float(offsetx),float(offsety)))
                        platform.rotatedegree = float(rotate)
                    
                    elif Feature == 'VISIABLE':
                        Type = row[1]
                        if Type == 'PAD':
                            Feature, Type, platformName, padName, visiable = row                          
                            platform:Platform = next((p for p in self.platforms if p.name == platformName), None)
                            if not platform:
                                raise ValueError(f"Platform not found for platformName: {platformName}")
                            pad:PAD = next((p for p in platform.pads if p.name == padName), None)
                            if not pad:
                                raise ValueError(f"pad not found for padName: {padName}")
                            pad.visible = True if visiable.upper() == 'TRUE' else False
                        
                    else:
                        raise ValueError(f"Invalid Feature type: {Feature} row={rowid}")
            else :
                raise ValueError(f"Bondingtable is not valid: {file_path}")
                

    def preview(self, paras:Paras):

        # Calculate vertices with scaling before drawing
        for platform in self.platforms:
            platform.reshape(paras)
            # Draw all platforms and wires on the canvas
            platform.preview(paras)            
            
        # Extract platforms and bonding tables from CSV files but do not calculate vertices yet            
        for wire in self.wires:
            wire.preview(canvas=paras.canvas,linewidth=5)
